package io.xxx.omni.sync.impl

import com.alibaba.fastjson.JSONObject
import io.xxx.omni.sync.Document
import io.xxx.omni.sync.PagePorter
import io.xxx.omni.sync.Store
import io.xxx.omni.util.offset
import io.xxx.omni.util.toLocalDateTime
import org.apache.commons.codec.digest.DigestUtils
import org.slf4j.LoggerFactory
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpMethod
import org.springframework.stereotype.Component
import org.springframework.util.LinkedMultiValueMap
import org.springframework.web.util.UriComponentsBuilder
import java.time.LocalDateTime
import java.util.*

/**
 * 小红书
 */
abstract class XhsPagePorter : PagePorter() {

    private val httpUrl = "https://ark.xiaohongshu.com"

    override val platformCode = "xiaohongshu"

    protected abstract val path: String

    protected fun getResponse(
        store: Store,
        path: String,
        params: Map<String, String> = emptyMap()
    ): JSONObject {
        val timestamp = (System.currentTimeMillis() / 1000).toString()
        val appKey = store.appKey!!
        val appSecret = store.appSecret!!
        val signMap = TreeMap(params)
        signMap["app-key"] = appKey
        signMap["timestamp"] = timestamp
        val builder = StringBuilder()
        signMap.forEach { (k, v) -> builder.append(k).append("=").append(v).append("&") }
        builder.deleteCharAt(builder.lastIndexOf("&")).append(appSecret)
        val sign = DigestUtils.md5Hex("$path?$builder")
        val headers = HttpHeaders()
        headers.add("timestamp", timestamp)
        headers.add("app-key", appKey)
        headers.add("sign", sign)
        val httpEntity = HttpEntity<Any>(null, headers)
        val queryParams = LinkedMultiValueMap<String, String>()
        params.forEach { (key, value) -> queryParams.add(key, value) }
        val url = UriComponentsBuilder.fromHttpUrl(httpUrl)
            .path(path)
            .queryParams(queryParams)
            .toUriString()
        val response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, JSONObject::class.java)
        if (response.statusCode.isError || response.body == null) {
            val msg = "接口[url: $url, headers: $headers, queryParams: $signMap]异常"
            throw RuntimeException(msg)
        }
        val body = response.body!!
        if (!body.getBoolean("success")) {
            val msg = "接口[url: $url, headers: $headers, queryParams: $params]异常: $body"
            log.error(msg)
            throwException(url, body.getString("error_code"), body.getString("error_msg"))
        }
        return response.body!!.getJSONObject("data")
    }

    companion object {
        private val log = LoggerFactory.getLogger(XhsPagePorter::class.java)
    }
}

@Component
class XhsTradePorter : XhsPagePorter() {

    override val path = "/ark/open_api/v0/packages"

    override val jobId = 1

    override fun getCount(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?
    ): Long? {
        val params = getParams(startTime, endTime, parameter as String)
        val response = getResponse(store, path, params)
        return response.getLong("total_number")
    }

    override fun getData(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?,
        pageNo: Long,
    ): List<Document>? {
        val params = getParams(startTime, endTime, parameter as String, pageNo)
        val response = getResponse(store, path, params)
        val packageList = response.getJSONArray("package_list")
        return packageList
            .map {
                @Suppress("unchecked_cast")
                val value = JSONObject(it as Map<String, Any>)
                Document(value.getString("package_id"), value, value.getLong("update_time").toLocalDateTime())
            }
    }

    override fun getParameters(store: Store): List<Any> {
        return listOf("waiting", "shipped", "received")
    }

    private fun getParams(
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        status: String,
        pageNo: Long = 1
    ): Map<String, String> {
        return mapOf(
            "time_type" to "updated_at",
            "start_time" to startTime.toEpochSecond(offset).toString(),
            "end_time" to endTime.toEpochSecond(offset).toString(),
            "page_no" to pageNo.toString(),
            "page_size" to pageSize.toString(),
            "status" to status,
        )
    }
}

@Component
class XhsRefundPorter : XhsPagePorter() {

    override val path = "/ark/open_api/v0/refund/list"

    override val jobId = 2

    override fun getCount(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?
    ): Long? {
        val params = getParams(startTime, endTime)
        val response = getResponse(store, path, params)
        return response.getLong("total_number")
    }

    override fun getData(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?,
        pageNo: Long,
    ): List<Document>? {
        val params = getParams(startTime, endTime, pageNo)
        val response = getResponse(store, path, params)
        return response.getJSONArray("refund_list")
            .map {
                @Suppress("unchecked_cast")
                val value = JSONObject(it as Map<String, Any>)
                val sn = value.getString("returns_id")
                val rsn = value.getString("package_id")
                val modified = value.getLong("updated_at")
                Document(sn, rsn, value, modified.toLocalDateTime())
            }
    }

    private fun getParams(
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        pageNo: Long = 1
    ): Map<String, String> {
        return mapOf(
            "start_time" to startTime.toEpochSecond(offset).toString(),
            "end_time" to endTime.toEpochSecond(offset).toString(),
            "page" to pageNo.toString(),
            "page_size" to pageSize.toString(),
        )
    }
}